// Copyright (c) [2019] [huazhonghua]
// [理解] is licensed under the Mulan PSL v1.
// You can use this software according to the terms and conditions of the Mulan PSL v1.
// You may obtain a copy of Mulan PSL v1 at:
//     http://license.coscl.org.cn/MulanPSL
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
// PURPOSE.
// See the Mulan PSL v1 for more details.

#ifndef OPERATIONS_H
#define OPERATIONS_H

#include <pthread.h>

#include "structure.h"
#include "basic.h"
#include "execution.h"

int run_operation(ExecutionData* execution, Node* node) {
	Node* operation = get_linked(node, OPERATION);
	Node* steps = get_chained(execution->all, (int[]){operation->part, STEP}, 2);
	push_new_run_data(execution, steps);
	link_node(execution->run_data, OPERATION, node);
	return OK;
}

int run_steps(ExecutionData* execution, Node* node) {
	Node* steps = get_linked(node, STEP);
	push_new_run_data(execution, steps);
	link_node(execution->run_data, OPERATION, node);
	return OK;
}

int get_variable(ExecutionData* execution, Node* node) {
	Node* from = get_linked(node, FROM);
	Node* index = get_linked(node, INDEX);
	Node* list_node = get_linked(execution->context, from->part);
	link_node(execution->request, RETURN, get_list_node_item(list_node, index->part));
	return OK;
}

int get_create_variable(ExecutionData* execution, Node* node) {
	Node* from = get_linked(node, FROM);
	Node* list_node = get_linked(execution->context, from->part);
	Node* index = get_linked(node, INDEX);
	Node* item = get_list_node_item(list_node, index->part);
	if(item == NULL) {
		item = create_node(execution->all);
		set_list_node_item(list_node, index->part, item);
	}
	link_node(execution->request, RETURN, item);
	return OK;
}

int eval_left(ExecutionData* execution, Node* step) {
	return run_node(execution, get_chained(execution->run_data, (int[]){OPERATION, LEFT}, 2));
}

int eval_right(ExecutionData* execution, Node* step) {
	return run_node(execution, get_chained(execution->run_data, (int[]){OPERATION, RIGHT}, 2));
}

int copy_goal_prepare_for_args_create_i(ExecutionData* execution, Node* step) {
	link_node(execution->run_data, GOAL, get_linked(execution->request, RETURN));
	Node* arg_exps = get_chained(execution->run_data, (int[]){OPERATION, ARGUMENT}, 2);
	link_node(execution->run_data, EXPRESSION_LIST, arg_exps);

	Node* dispose_list = get_linked(execution->run_data, DISPOSE_LIST);

	Node* arguments = create_list_node(execution->all, get_list_node_length(arg_exps));
	make_disposable(execution->all, dispose_list, arguments);
	link_node(execution->run_data, ARGUMENT, arguments);

	Node* i = create_node(execution->all);
	i->part = 0;
	make_disposable(execution->all, dispose_list, i);
	link_node(execution->run_data, I, i);
	return OK;
}

int check_finish_evaluate(ExecutionData* execution, Node* step) {
	Node* exp_list = get_linked(execution->run_data, EXPRESSION_LIST);
	int length = get_list_node_length(exp_list);
	Node* i = get_linked(execution->run_data, I);
	if(i->part >= length) execution->step_index->part += 3;
	return OK;
}

int eval_argument(ExecutionData* execution, Node* step) {
	Node* exp_list = get_linked(execution->run_data, EXPRESSION_LIST);
	List* list = exp_list->other.pointer;
	Node* i = get_linked(execution->run_data, I);
	return run_node(execution, list->nodes[i->part++]);
}

int add_argument(ExecutionData* execution, Node* step) {
	Node* list_node = get_linked(execution->run_data, ARGUMENT);
	Node* node = get_linked(execution->request, RETURN);
	add_to_list_node(list_node, node);

	Node* dispose_list = get_linked(execution->run_data, DISPOSE_LIST);
	add_to_list_node(dispose_list, node);

	return OK;
}

int jump(ExecutionData* execution, Node* step) {
	Node* offset = get_linked(step, OFFSET);
	execution->step_index->part += (offset->part - 1);
	return OK;
}

int choose_way(ExecutionData* execution, Node* step) {
	Node* run_data = execution->run_data;
	Node* goal = get_linked(run_data, GOAL);
	Node* ways = get_linked(goal, WAY);
	Node* chosen_way = get_list_node_head(ways);
	link_node(run_data, CHOSEN_WAY, chosen_way);
	return OK;
}

int load_new_context(ExecutionData* execution, Node* step) {
	Node* way = get_linked(execution->run_data, CHOSEN_WAY);
	Node* context = create_context(execution->all, execution->context_stack);
	execution->context = context;
	Node* args = get_linked(execution->run_data, ARGUMENT);
	link_node(context, ARGUMENT, args);
	link_node(context, GOAL, get_linked(execution->run_data, GOAL));

	Node* variables = get_linked(way, VARIABLE);
	if(variables != NULL) {
		int link_count = get_link_count(variables);
		Node* vars = create_list_node(execution->all, link_count);
		List* var_list = vars->other.pointer;
		var_list->length = link_count;

		link_node(context, VARIABLE, vars);

		link_node(vars, DISPOSABLE, get_int(execution->all, 1));
		Node* dispose_list = get_linked(context, DISPOSE_LIST);
		add_to_list_node(dispose_list, vars);
	}

	link_node(execution->run_data, CREATED_CONTEXT, context);
	Node* steps = get_linked(way, STEP);
	push_new_run_data(execution, steps);

	return OK;
}

int pop_context(ExecutionData* execution, Node* step) {
	List* context_list = execution->context_stack->other.pointer;
	if(context_list->length <= 0) {
		// error
		return OK;
	}
	--(context_list->length);

	Node* dispose_list = get_linked(execution->context, DISPOSE_LIST);
	dispose(dispose_list);

	free_node(execution->context);

	execution->context = get_list_node_head(execution->context_stack);

	return OK;
}

Node* make_runner(Node* all, int (*func)(ExecutionData*, Node*)) {
	Node* runner = create_node(all);
	runner->other.pointer = func;
	return runner;
}

void add_runner_step(Node* all, Node* steps, int (*func)(ExecutionData*, Node*)) {
	Node* step = create_node(all);
	link_node(step, RUNNER, make_runner(all, func));
	add_to_list_node(steps, step);
}

void add_goal_expression_steps(Node* all) {
	Node* steps = create_list_node(all, 9);

	add_runner_step(all, steps, eval_left);
	add_runner_step(all, steps, copy_goal_prepare_for_args_create_i);
	add_runner_step(all, steps, check_finish_evaluate);
	add_runner_step(all, steps, eval_argument);
	add_runner_step(all, steps, add_argument);

	Node* step = create_node(all);
	link_node(step, OFFSET, get_create_int(all, -3));
	link_node(step, RUNNER, make_runner(all, jump));
	add_to_list_node(steps, step);

	add_runner_step(all, steps, choose_way);

	/*Node* before = get_linked(goal, BEFORE);
	if(before != NULL) {
	}*/

	add_runner_step(all, steps, load_new_context);

	/*Node* after = get_linked(goal, AFTER);
	if(after != NULL) {
	}*/

	add_runner_step(all, steps, pop_context);

	Node* goal_expression = get_linked(all, GOAL_EXPRESSION);
	link_node(goal_expression, STEP, steps);
}

int run_create(ExecutionData* execution, Node* step) {
	Node* args = get_linked(execution->context, ARGUMENT);
	List* list = args->other.pointer;

	Node* node = create_instance(execution->all, list->nodes[0]);
	Node* all = get_linked(execution->context, ALL);
	add_string(all, node, list->nodes[1]);

	return OK;
}

int eval_arg_0(ExecutionData* execution, Node* step) {
	Node* right = get_chained(execution->run_data, (int[]){OPERATION, RIGHT}, 2);
	List* list = right->other.pointer;
	return run_node(execution, list->nodes[0]);
}

int run_if(ExecutionData* execution, Node* step) {
	Node* node = get_linked(execution->request, RETURN);
	Node* args = get_chained(execution->run_data, (int[]){OPERATION, RIGHT}, 2);
	List* list = args->other.pointer;

	Node* steps = NULL;
	if(node->part != 0) {
		steps = list->nodes[1];
	}
	else if(list->length > 2) {
		steps = list->nodes[2];
	}

	Node* dispose_list = get_linked(execution->run_data, DISPOSE_LIST);
	add_to_list_node(dispose_list, node);

	if(steps != NULL) {
		push_new_run_data(execution, steps);
	}
	return OK;
}

void add_if_steps(Node* all) {
	Node* steps = create_list_node(all, 2);

	add_runner_step(all, steps, eval_arg_0);
	add_runner_step(all, steps, run_if);

	link_node(get_linked(all, IF), STEP, steps);
}

void link_variables(Node* all, Node* goal, Node* node, Node* var_index) {
	if(node == NULL) return;

	if(is_instance_of(node, LIST)) {
		List* list = node->other.pointer;
		for(int i = 0; i < list->length; ++i) {
			Node* arg = list->nodes[i];
			link_variables(all, goal, arg, var_index);
		}
	}
	else if(is_instance_of(node, VARIABLE)) {
		Node* string = get_linked(node, STRING);
		Node* parameters = goal == NULL ? NULL : get_linked(goal, PARAMETER);

		Node* parameter = NULL;
		int i;
		if(parameters != NULL) {
			int length = get_list_node_length(parameters);
			for(i = 0; i < length; ++i) {
				Node* item = get_list_node_item(parameters, i);
				Node* param_str = get_linked(item, STRING);
				if(string->part == param_str->part) {
					parameter = item;
					break;
				}
			}
		}

		if(parameter == NULL) {
			Node* index = get_linked(var_index, string->part);
			if(index == NULL) {
				index = get_create_int(all, get_link_count(var_index));
				link_node(var_index, string->part, index);
			}
			link_node(node, FROM, get_linked(all, VARIABLE));
			link_node(node, INDEX, index);
		}
		else {
			link_node(node, FROM, get_linked(all, ARGUMENT));
			link_node(node, INDEX, get_create_int(all, i));
		}
	}

	Node* left = get_linked(node, LEFT);
	link_variables(all, goal, left, var_index);

	Node* right = get_linked(node, RIGHT);
	link_variables(all, goal, right, var_index);

	Node* arguments = get_linked(node, ARGUMENT);
	link_variables(all, goal, arguments, var_index);
}

void link_way_variables(Node* all, Node* goal, Node* way) {
	Node* var_index = get_linked(way, VARIABLE);
	if(var_index != NULL) return;

	var_index = create_node(all);
	link_node(way, VARIABLE, var_index);

	Node* steps = get_linked(way, STEP);

	link_variables(all, goal, steps, var_index);
}

int add_way(ExecutionData* execution, Node* step) {
	Node* goal = get_linked(execution->request, RETURN);
	// check

	Node* args = get_chained(execution->run_data, (int[]){OPERATION, RIGHT}, 2);
	List* list = args->other.pointer;

	Node* ways = get_linked(goal, WAY);
	if(ways == NULL) {
		ways = create_list_node(execution->all, 1);
		link_node(goal, WAY, ways);
	}

	Node* way = create_node(execution->all);
	add_to_list_node(ways, way);

	Node* steps = list->nodes[1];
	link_node(way, STEP, steps);

	link_way_variables(execution->all, goal, way);

	return OK;
}

void add_add_way_steps(Node* all) {
	Node* steps = create_list_node(all, 2);

	add_runner_step(all, steps, eval_arg_0);
	add_runner_step(all, steps, add_way);

	link_node(get_linked(all, ADD_WAY), STEP, steps);
}

int eval_assign_link_array_from(ExecutionData* execution, Node* step) {
	Node* left = get_chained(execution->run_data, (int[]){OPERATION, LEFT}, 2);
	link_node(execution->run_data, TAIL, get_linked(left, RIGHT));
	return run_node(execution, get_linked(left, LEFT));
}

int copy_from_evaluate_tail(ExecutionData* execution, Node* step) {
	Node* from = get_linked(execution->request, RETURN);
	if(from != NULL) {
		link_node(execution->run_data, FROM, from);
	}
	return run_node(execution, get_linked(execution->run_data, TAIL));
}

int copy_tail_evaluate_right(ExecutionData* execution, Node* step) {
	link_node(execution->run_data, TAIL, get_linked(execution->request, RETURN));
	return run_node(execution, get_chained(execution->run_data, (int[]){OPERATION, RIGHT}, 2));
}

int assign_link(ExecutionData* execution, Node* step) {
	Node* from = get_linked(execution->run_data, FROM);
	Node* tail = get_linked(execution->run_data, TAIL);
	link_node(from, tail->part, get_linked(execution->request, RETURN));
	return OK;
}

int assign_array_item(ExecutionData* execution, Node* step) {
	Node* from = get_linked(execution->run_data, FROM);
	Node* tail = get_linked(execution->run_data, TAIL);
	set_list_node_item(from, tail->part, get_linked(execution->request, RETURN));
	return OK;
}

int assign_var(ExecutionData* execution, Node* node) {
	Node* var_exp = get_chained(execution->run_data, (int[]){OPERATION, LEFT}, 2);
	Node* from = get_linked(var_exp, FROM);
	Node* index = get_linked(var_exp, INDEX);

	Node* list_node = get_linked(execution->context, from->part);
	List* list = list_node->other.pointer;
	Node* value = get_linked(execution->request, RETURN);
	list->nodes[index->part] = value;

	Node* dispose_list = get_linked(execution->context, DISPOSE_LIST);
	add_to_list_node(dispose_list, value);

	return OK;
}

void add_assign_variable_steps(Node* all) {
	Node* steps = create_list_node(all, 2);

	add_runner_step(all, steps, eval_right);
	add_runner_step(all, steps, assign_var);

	link_node(all, ASSIGN_VARIABLE_STEP, steps);
}

void add_assign_link_steps(Node* all) {
	Node* steps = create_list_node(all, 4);

	add_runner_step(all, steps, eval_assign_link_array_from);
	add_runner_step(all, steps, copy_from_evaluate_tail);
	add_runner_step(all, steps, copy_tail_evaluate_right);
	add_runner_step(all, steps, assign_link);

	link_node(all, ASSIGN_LINK_STEP, steps);
}

void add_assign_array_item_steps(Node* all) {
	Node* steps = create_list_node(all, 4);

	add_runner_step(all, steps, eval_assign_link_array_from);
	add_runner_step(all, steps, copy_from_evaluate_tail);
	add_runner_step(all, steps, copy_tail_evaluate_right);
	add_runner_step(all, steps, assign_array_item);

	link_node(all, ASSIGN_ARRAY_ITEM_STEP, steps);
}

int copy_left_eval_right(ExecutionData* execution, Node* step) {
	Node* left = get_linked(execution->request, RETURN);
	link_node(execution->run_data, LEFT, left);
	return run_node(execution, get_chained(execution->run_data, (int[]){OPERATION, RIGHT}, 2));
}

int get_array_item(ExecutionData* execution, Node* step) {
	Node* left = get_linked(execution->run_data, LEFT);
	Node* right = get_linked(execution->request, RETURN);
	Node* node = get_list_node_item(left, right->part);
	link_node(execution->request, RETURN, node);
	return OK;
}

int get_create_array_item(ExecutionData* execution, Node* step) {
	Node* left = get_linked(execution->run_data, LEFT);
	Node* right = get_linked(execution->request, RETURN);
	if(left->other.pointer == NULL) {
		make_list_node(execution->all, left, right->part + 1);
	}

	Node* node = get_list_node_item(left, right->part);
	if(node == NULL) {
		node = create_node(execution->all);
		set_list_node_item(left, right->part, node);
	}

	link_node(execution->request, RETURN, node);
	return OK;
}

void add_get_array_item_steps(Node* all) {
	Node* steps = create_list_node(all, 3);

	add_runner_step(all, steps, eval_left);
	add_runner_step(all, steps, copy_left_eval_right);
	add_runner_step(all, steps, get_array_item);

	link_node(all, GET_ARRAY_ITEM_STEP, steps);
}

void add_get_create_array_item_steps(Node* all) {
	Node* steps = create_list_node(all, 3);

	add_runner_step(all, steps, eval_left);
	add_runner_step(all, steps, copy_left_eval_right);
	add_runner_step(all, steps, get_create_array_item);

	link_node(all, GET_CREATE_ARRAY_ITEM_STEP, steps);
}

int return_get_linked(ExecutionData* execution, Node* step) {
	Node* left = get_linked(execution->run_data, LEFT);
	Node* right = get_linked(execution->request, RETURN);
	Node* node = get_linked(left, right->part);
	link_node(execution->request, RETURN, node);
	return OK;
}

int return_get_create_linked(ExecutionData* execution, Node* step) {
	Node* left = get_linked(execution->run_data, LEFT);
	Node* right = get_linked(execution->request, RETURN);
	Node* node = get_linked(left, right->part);
	if(node == NULL) {
		node = create_node(execution->all);
		link_node(left, right->part, node);
	}
	link_node(execution->request, RETURN, node);
	return OK;
}

void add_get_linked_steps(Node* all) {
	Node* steps = create_list_node(all, 3);

	add_runner_step(all, steps, eval_left);
	add_runner_step(all, steps, copy_left_eval_right);
	add_runner_step(all, steps, return_get_linked);

	link_node(all, GET_LINKED_STEP, steps);
}

void add_get_create_linked_steps(Node* all) {
	Node* steps = create_list_node(all, 3);

	add_runner_step(all, steps, eval_left);
	add_runner_step(all, steps, copy_left_eval_right);
	add_runner_step(all, steps, return_get_create_linked);

	link_node(all, GET_CREATE_LINKED_STEP, steps);
}

int return_less(ExecutionData* execution, Node* step) {
	Node* left = get_linked(execution->run_data, LEFT);
	Node* right = get_linked(execution->request, RETURN);

	Node* node = create_node(execution->all);
	node->part = left->part < right->part;
	link_node(node, DISPOSABLE, get_int(execution->all, 1));
	link_node(execution->request, RETURN, node);

	return OK;
}

void add_less_steps(Node* all) {
	Node* steps = create_list_node(all, 3);

	add_runner_step(all, steps, eval_left);
	add_runner_step(all, steps, copy_left_eval_right);
	add_runner_step(all, steps, return_less);

	link_node(get_linked(all, LESS), STEP, steps);
}

int return_greater(ExecutionData* execution, Node* step) {
	Node* left = get_linked(execution->run_data, LEFT);
	Node* right = get_linked(execution->request, RETURN);

	Node* node = create_node(execution->all);
	node->part = left->part > right->part;
	link_node(node, DISPOSABLE, get_int(execution->all, 1));
	link_node(execution->request, RETURN, node);

	return OK;
}

void add_greater_steps(Node* all) {
	Node* steps = create_list_node(all, 3);

	add_runner_step(all, steps, eval_left);
	add_runner_step(all, steps, copy_left_eval_right);
	add_runner_step(all, steps, return_greater);

	link_node(get_linked(all, GREATER), STEP, steps);
}

int return_less_equal(ExecutionData* execution, Node* step) {
	Node* left = get_linked(execution->run_data, LEFT);
	Node* right = get_linked(execution->request, RETURN);

	Node* node = create_node(execution->all);
	node->part = left->part <= right->part;
	link_node(node, DISPOSABLE, get_int(execution->all, 1));
	link_node(execution->request, RETURN, node);

	return OK;
}

void add_less_equal_steps(Node* all) {
	Node* steps = create_list_node(all, 3);

	add_runner_step(all, steps, eval_left);
	add_runner_step(all, steps, copy_left_eval_right);
	add_runner_step(all, steps, return_less_equal);

	link_node(get_linked(all, LESS_EQUAL), STEP, steps);
}

int return_greater_equal(ExecutionData* execution, Node* step) {
	Node* left = get_linked(execution->run_data, LEFT);
	Node* right = get_linked(execution->request, RETURN);

	Node* node = create_node(execution->all);
	node->part = left->part >= right->part;
	link_node(node, DISPOSABLE, get_int(execution->all, 1));
	link_node(execution->request, RETURN, node);

	return OK;
}

void add_greater_equal_steps(Node* all) {
	Node* steps = create_list_node(all, 3);

	add_runner_step(all, steps, eval_left);
	add_runner_step(all, steps, copy_left_eval_right);
	add_runner_step(all, steps, return_greater_equal);

	link_node(get_linked(all, GREATER_EQUAL), STEP, steps);
}

int return_not_equal(ExecutionData* execution, Node* step) {
	Node* left = get_linked(execution->run_data, LEFT);
	Node* right = get_linked(execution->request, RETURN);

	Node* node = create_node(execution->all);
	node->part = left->part != right->part;
	link_node(node, DISPOSABLE, get_int(execution->all, 1));
	link_node(execution->request, RETURN, node);

	return OK;
}

void add_not_equal_steps(Node* all) {
	Node* steps = create_list_node(all, 3);

	add_runner_step(all, steps, eval_left);
	add_runner_step(all, steps, copy_left_eval_right);
	add_runner_step(all, steps, return_not_equal);

	link_node(get_linked(all, NOT_EQUAL), STEP, steps);
}

int return_equal(ExecutionData* execution, Node* step) {
	Node* left = get_linked(execution->run_data, LEFT);
	Node* right = get_linked(execution->request, RETURN);

	Node* node = create_node(execution->all);
	node->part = left->part == right->part;
	link_node(node, DISPOSABLE, get_int(execution->all, 1));
	link_node(execution->request, RETURN, node);

	return OK;
}

void add_equal_steps(Node* all) {
	Node* steps = create_list_node(all, 3);

	add_runner_step(all, steps, eval_left);
	add_runner_step(all, steps, copy_left_eval_right);
	add_runner_step(all, steps, return_equal);

	link_node(get_linked(all, EQUAL), STEP, steps);
}

int return_and(ExecutionData* execution, Node* step) {
	Node* left = get_linked(execution->run_data, LEFT);
	Node* right = get_linked(execution->request, RETURN);

	Node* node = create_node(execution->all);
	node->part = left->part && right->part;
	link_node(node, DISPOSABLE, get_int(execution->all, 1));
	link_node(execution->request, RETURN, node);

	return OK;
}

void add_and_steps(Node* all) {
	Node* steps = create_list_node(all, 3);

	add_runner_step(all, steps, eval_left);
	add_runner_step(all, steps, copy_left_eval_right);
	add_runner_step(all, steps, return_and);

	link_node(get_linked(all, AND), STEP, steps);
}

int return_or(ExecutionData* execution, Node* step) {
	Node* left = get_linked(execution->run_data, LEFT);
	Node* right = get_linked(execution->request, RETURN);

	Node* node = create_node(execution->all);
	node->part = left->part || right->part;
	link_node(node, DISPOSABLE, get_int(execution->all, 1));
	link_node(execution->request, RETURN, node);

	return OK;
}

void add_or_steps(Node* all) {
	Node* steps = create_list_node(all, 3);

	add_runner_step(all, steps, eval_left);
	add_runner_step(all, steps, copy_left_eval_right);
	add_runner_step(all, steps, return_or);

	link_node(get_linked(all, OR), STEP, steps);
}


int eval_return_right(ExecutionData* execution, Node* step) {
	Node* right = get_chained(execution->run_data, (int[]){OPERATION, RIGHT}, 2);
	if(right == NULL) {
		unlink_node(execution->request, RETURN);
		return OK;
	}
	return run_node(execution, right);
}

int do_return(ExecutionData* execution, Node* step) {
	while(pop_run_data(execution)) {
		Node* created = get_linked(execution->run_data, CREATED_CONTEXT);
		if(created != NULL) break;
	}
	return OK;
}

void add_return_steps(Node* all) {
	Node* steps = create_list_node(all, 2);

	add_runner_step(all, steps, eval_return_right);
	add_runner_step(all, steps, do_return);

	link_node(get_linked(all, RETURN), STEP, steps);
}

int return_minus(ExecutionData* execution, Node* step) {
	Node* left = get_linked(execution->run_data, LEFT);
	Node* right = get_linked(execution->request, RETURN);

	Node* node = create_node(execution->all);
	node->part = left->part - right->part;
	link_node(node, DISPOSABLE, get_int(execution->all, 1));
	link_node(execution->request, RETURN, node);

	return OK;
}

void add_minus_steps(Node* all) {
	Node* steps = create_list_node(all, 3);

	add_runner_step(all, steps, eval_left);
	add_runner_step(all, steps, copy_left_eval_right);
	add_runner_step(all, steps, return_minus);

	link_node(get_linked(all, MINUS), STEP, steps);
}

int return_plus(ExecutionData* execution, Node* step) {
	Node* left = get_linked(execution->run_data, LEFT);
	Node* right = get_linked(execution->request, RETURN);

	int num = left->part + right->part;
	Node* node = create_node(execution->all);
	node->part = num;
	link_node(node, DISPOSABLE, get_int(execution->all, 1));
	link_node(execution->request, RETURN, node);

	return OK;
}

void add_plus_steps(Node* all) {
	Node* steps = create_list_node(all, 3);

	add_runner_step(all, steps, eval_left);
	add_runner_step(all, steps, copy_left_eval_right);
	add_runner_step(all, steps, return_plus);

	link_node(get_linked(all, PLUS), STEP, steps);
}

int return_multiply(ExecutionData* execution, Node* step) {
	Node* left = get_linked(execution->run_data, LEFT);
	Node* right = get_linked(execution->request, RETURN);

	int num = left->part * right->part;
	Node* node = create_node(execution->all);
	node->part = num;
	link_node(node, DISPOSABLE, get_int(execution->all, 1));
	link_node(execution->request, RETURN, node);

	return OK;
}

void add_multiply_steps(Node* all) {
	Node* steps = create_list_node(all, 3);

	add_runner_step(all, steps, eval_left);
	add_runner_step(all, steps, copy_left_eval_right);
	add_runner_step(all, steps, return_multiply);

	link_node(get_linked(all, MULTIPLY), STEP, steps);
}

int return_divide(ExecutionData* execution, Node* step) {
	Node* left = get_linked(execution->run_data, LEFT);
	Node* right = get_linked(execution->request, RETURN);

	int num = left->part / right->part;
	Node* node = create_node(execution->all);
	node->part = num;
	link_node(node, DISPOSABLE, get_int(execution->all, 1));
	link_node(execution->request, RETURN, node);

	return OK;
}

void add_divide_steps(Node* all) {
	Node* steps = create_list_node(all, 3);

	add_runner_step(all, steps, eval_left);
	add_runner_step(all, steps, copy_left_eval_right);
	add_runner_step(all, steps, return_divide);

	link_node(get_linked(all, DIVIDE), STEP, steps);
}

int return_mod(ExecutionData* execution, Node* step) {
	Node* left = get_linked(execution->run_data, LEFT);
	Node* right = get_linked(execution->request, RETURN);

	int num = left->part % right->part;
	Node* node = create_node(execution->all);
	node->part = num;
	link_node(node, DISPOSABLE, get_int(execution->all, 1));
	link_node(execution->request, RETURN, node);

	return OK;
}

void add_mod_steps(Node* all) {
	Node* steps = create_list_node(all, 3);

	add_runner_step(all, steps, eval_left);
	add_runner_step(all, steps, copy_left_eval_right);
	add_runner_step(all, steps, return_mod);

	link_node(get_linked(all, MODULO), STEP, steps);
}

void increment_done_count(Node* request) {
/*Node* log_node = get_linked(request, LOG);
FILE* log = log_node->other.pointer;*/

	Node* unordered = get_linked(request, UNORDERED);

	Node* lock = get_linked(unordered, LOCK);
	pthread_mutex_t* mutex = lock->other.pointer;

	Node* count = get_linked(unordered, COUNT);
	Node* total = get_linked(unordered, TOTAL);

	pthread_mutex_lock(mutex);

	++(count->part);
	int is_done = count->part >= total->part;

	pthread_mutex_unlock(mutex);

	if(is_done) {
		Node* unordered_request = get_linked(unordered, REQUEST);
		Node* all = get_linked(unordered_request, ALL);
		queue_work(all, unordered_request);
	}
}

int run_unordered(ExecutionData* execution, Node* step) {
	Node* unordered = get_linked(execution->run_data, UNORDERED);

	if(unordered == NULL) {
		unordered = create_node(execution->all);
		link_node(execution->run_data, UNORDERED, unordered);

		link_node(unordered, REQUEST, execution->request);

		Node* right = get_linked(step, RIGHT);
		Node* total = create_node(execution->all);
		total->part = get_list_node_length(right);
		link_node(unordered, TOTAL, total);

		Node* count = create_node(execution->all);
		count->part = 0;
		link_node(unordered, COUNT, count);

		create_lock(execution->all, unordered);

		Node* callback = create_node(execution->all);
		callback->other.pointer = increment_done_count;
		link_node(unordered, CALLBACK, callback);

		Node* requests = create_list_node(execution->all, total->part);
		link_node(unordered, CHILD_REQUEST, requests);

		for(int i = 0; i < total->part; ++i) {
			Node* context_stack = create_list_node(execution->all, 1);
			add_to_list_node(context_stack, execution->context);

			Node* steps = get_list_node_item(right, i);
			Node* request = create_request(execution->all, context_stack, steps);
			add_to_list_node(requests, request);

			link_node(request, UNORDERED, unordered);
			link_node(request, CALLBACK, callback);

			queue_work(execution->all, request);
		}

		return WAITING;
	}
	else {
		Node* requests = get_linked(unordered, CHILD_REQUEST);
		Node* total = get_linked(unordered, TOTAL);

		for(int i = 0; i < total->part; ++i) {
			free_request(get_list_node_item(requests, i));
		}

		Node* callback = get_linked(unordered, CALLBACK);
		free_node(callback);

		free_list_node(requests);

		delete_lock(unordered);

		free_node(get_linked(unordered, COUNT));

		free_node(total);

		free_node(unordered);
	}

	return OK;
}

#endif
